// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "skia/ext/benchmarking_canvas.h"

#include <memory>
#include <utility>

#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "base/memory/ptr_util.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkImageFilter.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "third_party/skia/include/core/SkString.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/core/SkXfermode.h"

namespace {

class FlagsBuilder {
public:
    FlagsBuilder(char separator)
        : separator_(separator)
    {
    }

    void addFlag(bool flag_val, const char flag_name[])
    {
        if (!flag_val)
            return;
        if (!oss_.str().empty())
            oss_ << separator_;

        oss_ << flag_name;
    }

    std::string str() const
    {
        return oss_.str();
    }

private:
    char separator_;
    std::ostringstream oss_;
};

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(bool b)
{
    std::unique_ptr<base::FundamentalValue> val(new base::FundamentalValue(b));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(SkScalar scalar)
{
    std::unique_ptr<base::FundamentalValue> val(
        new base::FundamentalValue(scalar));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkSize& size)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("width", AsValue(size.width()));
    val->Set("height", AsValue(size.height()));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkPoint& point)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("x", AsValue(point.x()));
    val->Set("y", AsValue(point.y()));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkRect& rect)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("left", AsValue(rect.fLeft));
    val->Set("top", AsValue(rect.fTop));
    val->Set("right", AsValue(rect.fRight));
    val->Set("bottom", AsValue(rect.fBottom));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkRRect& rrect)
{
    std::unique_ptr<base::DictionaryValue> radii_val(new base::DictionaryValue());
    radii_val->Set("upper-left", AsValue(rrect.radii(SkRRect::kUpperLeft_Corner)));
    radii_val->Set("upper-right", AsValue(rrect.radii(SkRRect::kUpperRight_Corner)));
    radii_val->Set("lower-right", AsValue(rrect.radii(SkRRect::kLowerRight_Corner)));
    radii_val->Set("lower-left", AsValue(rrect.radii(SkRRect::kLowerLeft_Corner)));

    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("rect", AsValue(rrect.rect()));
    val->Set("radii", std::move(radii_val));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkMatrix& matrix)
{
    std::unique_ptr<base::ListValue> val(new base::ListValue());
    for (int i = 0; i < 9; ++i)
        val->Append(AsValue(matrix[i])); // no scoped_ptr-aware Append() variant

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(SkColor color)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->SetInteger("a", SkColorGetA(color));
    val->SetInteger("r", SkColorGetR(color));
    val->SetInteger("g", SkColorGetG(color));
    val->SetInteger("b", SkColorGetB(color));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(SkXfermode::Mode mode)
{
    std::unique_ptr<base::StringValue> val(
        new base::StringValue(SkXfermode::ModeName(mode)));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(SkCanvas::PointMode mode)
{
    static const char* gModeStrings[] = { "Points", "Lines", "Polygon" };
    DCHECK_LT(static_cast<size_t>(mode), SK_ARRAY_COUNT(gModeStrings));

    std::unique_ptr<base::StringValue> val(
        new base::StringValue(gModeStrings[mode]));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkXfermode& xfermode)
{
    SkXfermode::Mode mode;
    if (xfermode.asMode(&mode))
        return AsValue(mode);

    std::unique_ptr<base::StringValue> val(new base::StringValue("unknown"));
    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkColorFilter& filter)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());

    if (unsigned flags = filter.getFlags()) {
        FlagsBuilder builder('|');
        builder.addFlag(flags & SkColorFilter::kAlphaUnchanged_Flag,
            "kAlphaUnchanged_Flag");

        val->SetString("flags", builder.str());
    }

    SkScalar color_matrix[20];
    if (filter.asColorMatrix(color_matrix)) {
        std::unique_ptr<base::ListValue> color_matrix_val(new base::ListValue());
        for (unsigned i = 0; i < 20; ++i)
            color_matrix_val->Append(AsValue(color_matrix[i]));

        val->Set("color_matrix", std::move(color_matrix_val));
    }

    SkColor color;
    SkXfermode::Mode mode;
    if (filter.asColorMode(&color, &mode)) {
        std::unique_ptr<base::DictionaryValue> color_mode_val(
            new base::DictionaryValue());
        color_mode_val->Set("color", AsValue(color));
        color_mode_val->Set("mode", AsValue(mode));

        val->Set("color_mode", std::move(color_mode_val));
    }

    if (filter.asComponentTable(nullptr)) {
        std::unique_ptr<base::DictionaryValue> component_table_val(
            new base::DictionaryValue());
        // use this as a marker for now
        val->Set("component_table", std::move(component_table_val));
    }

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkImageFilter& filter)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->SetInteger("inputs", filter.countInputs());

    SkColorFilter* color_filter;
    if (filter.asColorFilter(&color_filter)) {
        val->Set("color_filter", AsValue(*color_filter));
        SkSafeUnref(color_filter); // ref'd in asColorFilter
    }

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkPaint& paint)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    SkPaint default_paint;

    if (paint.getColor() != default_paint.getColor())
        val->Set("Color", AsValue(paint.getColor()));

    if (paint.getStyle() != default_paint.getStyle()) {
        static const char* gStyleStrings[] = { "Fill", "Stroke", "StrokeFill" };
        DCHECK_LT(static_cast<size_t>(paint.getStyle()),
            SK_ARRAY_COUNT(gStyleStrings));
        val->SetString("Style", gStyleStrings[paint.getStyle()]);
    }

    if (paint.getXfermode() != default_paint.getXfermode()) {
        DCHECK(paint.getXfermode());
        val->Set("Xfermode", AsValue(*paint.getXfermode()));
    }

    if (paint.getFlags()) {
        FlagsBuilder builder('|');
        builder.addFlag(paint.isAntiAlias(), "AntiAlias");
        builder.addFlag(paint.isDither(), "Dither");
        builder.addFlag(paint.isUnderlineText(), "UnderlineText");
        builder.addFlag(paint.isStrikeThruText(), "StrikeThruText");
        builder.addFlag(paint.isFakeBoldText(), "FakeBoldText");
        builder.addFlag(paint.isLinearText(), "LinearText");
        builder.addFlag(paint.isSubpixelText(), "SubpixelText");
        builder.addFlag(paint.isDevKernText(), "DevKernText");
        builder.addFlag(paint.isLCDRenderText(), "LCDRenderText");
        builder.addFlag(paint.isEmbeddedBitmapText(), "EmbeddedBitmapText");
        builder.addFlag(paint.isAutohinted(), "Autohinted");
        builder.addFlag(paint.isVerticalText(), "VerticalText");

        val->SetString("Flags", builder.str());
    }

    if (paint.getFilterQuality() != default_paint.getFilterQuality()) {
        static const char* gFilterQualityStrings[] = {
            "None", "Low", "Medium", "High"
        };
        DCHECK_LT(static_cast<size_t>(paint.getFilterQuality()),
            SK_ARRAY_COUNT(gFilterQualityStrings));
        val->SetString("FilterLevel",
            gFilterQualityStrings[paint.getFilterQuality()]);
    }

    if (paint.getTextSize() != default_paint.getTextSize())
        val->SetDouble("TextSize", paint.getTextSize());

    if (paint.getTextScaleX() != default_paint.getTextScaleX())
        val->SetDouble("TextScaleX", paint.getTextScaleX());

    if (paint.getTextSkewX() != default_paint.getTextSkewX())
        val->SetDouble("TextSkewX", paint.getTextSkewX());

    if (paint.getColorFilter())
        val->Set("ColorFilter", AsValue(*paint.getColorFilter()));

    if (paint.getImageFilter())
        val->Set("ImageFilter", AsValue(*paint.getImageFilter()));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> SaveLayerFlagsAsValue(
    SkCanvas::SaveLayerFlags flags)
{
    FlagsBuilder builder('|');
    builder.addFlag(flags & SkCanvas::kIsOpaque_SaveLayerFlag,
        "kIsOpaque");
    builder.addFlag(flags & SkCanvas::kPreserveLCDText_SaveLayerFlag,
        "kPreserveLCDText");

    std::unique_ptr<base::StringValue> val(new base::StringValue(builder.str()));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(SkRegion::Op op)
{
    static const char* gOpStrings[] = { "Difference",
        "Intersect",
        "Union",
        "XOR",
        "ReverseDifference",
        "Replace" };
    DCHECK_LT(static_cast<size_t>(op), SK_ARRAY_COUNT(gOpStrings));
    std::unique_ptr<base::StringValue> val(new base::StringValue(gOpStrings[op]));
    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkRegion& region)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("bounds", AsValue(SkRect::Make(region.getBounds())));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkBitmap& bitmap)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("size", AsValue(SkSize::Make(bitmap.width(), bitmap.height())));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkImage& image)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("size", AsValue(SkSize::Make(image.width(), image.height())));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkTextBlob& blob)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
    val->Set("bounds", AsValue(blob.bounds()));

    return std::move(val);
}

WARN_UNUSED_RESULT
std::unique_ptr<base::Value> AsValue(const SkPath& path)
{
    std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());

    static const char* gFillStrings[] = { "winding", "even-odd", "inverse-winding", "inverse-even-odd" };
    DCHECK_LT(static_cast<size_t>(path.getFillType()),
        SK_ARRAY_COUNT(gFillStrings));
    val->SetString("fill-type", gFillStrings[path.getFillType()]);

    static const char* gConvexityStrings[] = { "Unknown", "Convex", "Concave" };
    DCHECK_LT(static_cast<size_t>(path.getConvexity()),
        SK_ARRAY_COUNT(gConvexityStrings));
    val->SetString("convexity", gConvexityStrings[path.getConvexity()]);

    val->SetBoolean("is-rect", path.isRect(nullptr));
    val->Set("bounds", AsValue(path.getBounds()));

    static const char* gVerbStrings[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
    static const int gPtsPerVerb[] = { 1, 1, 2, 2, 3, 0, 0 };
    static const int gPtOffsetPerVerb[] = { 0, 1, 1, 1, 1, 0, 0 };
    static_assert(
        SK_ARRAY_COUNT(gVerbStrings) == static_cast<size_t>(SkPath::kDone_Verb + 1),
        "gVerbStrings size mismatch");
    static_assert(
        SK_ARRAY_COUNT(gVerbStrings) == SK_ARRAY_COUNT(gPtsPerVerb),
        "gPtsPerVerb size mismatch");
    static_assert(
        SK_ARRAY_COUNT(gVerbStrings) == SK_ARRAY_COUNT(gPtOffsetPerVerb),
        "gPtOffsetPerVerb size mismatch");

    std::unique_ptr<base::ListValue> verbs_val(new base::ListValue());
    SkPath::Iter iter(const_cast<SkPath&>(path), false);
    SkPoint points[4];

    for (SkPath::Verb verb = iter.next(points, false);
         verb != SkPath::kDone_Verb; verb = iter.next(points, false)) {
        DCHECK_LT(static_cast<size_t>(verb), SK_ARRAY_COUNT(gVerbStrings));

        std::unique_ptr<base::DictionaryValue> verb_val(
            new base::DictionaryValue());
        std::unique_ptr<base::ListValue> pts_val(new base::ListValue());

        for (int i = 0; i < gPtsPerVerb[verb]; ++i)
            pts_val->Append(AsValue(points[i + gPtOffsetPerVerb[verb]]));

        verb_val->Set(gVerbStrings[verb], std::move(pts_val));

        if (SkPath::kConic_Verb == verb)
            verb_val->Set("weight", AsValue(iter.conicWeight()));

        verbs_val->Append(std::move(verb_val));
    }
    val->Set("verbs", std::move(verbs_val));

    return std::move(val);
}

template <typename T>
WARN_UNUSED_RESULT std::unique_ptr<base::Value> AsListValue(const T array[],
    size_t count)
{
    std::unique_ptr<base::ListValue> val(new base::ListValue());

    for (size_t i = 0; i < count; ++i)
        val->Append(AsValue(array[i]));

    return std::move(val);
}

class OverdrawXfermode : public SkXfermode {
public:
    SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override
    {
        // This table encodes the color progression of the overdraw visualization
        static const SkPMColor gTable[] = {
            SkPackARGB32(0x00, 0x00, 0x00, 0x00),
            SkPackARGB32(0xFF, 128, 158, 255),
            SkPackARGB32(0xFF, 170, 185, 212),
            SkPackARGB32(0xFF, 213, 195, 170),
            SkPackARGB32(0xFF, 255, 192, 127),
            SkPackARGB32(0xFF, 255, 185, 85),
            SkPackARGB32(0xFF, 255, 165, 42),
            SkPackARGB32(0xFF, 255, 135, 0),
            SkPackARGB32(0xFF, 255, 95, 0),
            SkPackARGB32(0xFF, 255, 50, 0),
            SkPackARGB32(0xFF, 255, 0, 0)
        };

        size_t idx;
        if (SkColorGetR(dst) < 64) { // 0
            idx = 0;
        } else if (SkColorGetG(dst) < 25) { // 10
            idx = 9; // cap at 9 for upcoming increment
        } else if ((SkColorGetB(dst) + 21) / 42 > 0) { // 1-6
            idx = 7 - (SkColorGetB(dst) + 21) / 42;
        } else { // 7-9
            idx = 10 - (SkColorGetG(dst) + 22) / 45;
        }

        ++idx;
        SkASSERT(idx < SK_ARRAY_COUNT(gTable));

        return gTable[idx];
    }

    Factory getFactory() const override { return NULL; }
#ifndef SK_IGNORE_TO_STRING
    void toString(SkString* str) const override
    {
        str->set("OverdrawXfermode");
    }
#endif
};

} // namespace

namespace skia {

class BenchmarkingCanvas::AutoOp {
public:
    // AutoOp objects are always scoped within draw call frames,
    // so the paint is guaranteed to be valid for their lifetime.
    AutoOp(BenchmarkingCanvas* canvas, const char op_name[],
        const SkPaint* paint = nullptr)
        : canvas_(canvas)
        , op_record_(new base::DictionaryValue())
        , op_params_(new base::ListValue())
    {

        DCHECK(canvas);
        DCHECK(op_name);

        op_record_->SetString("cmd_string", op_name);
        op_record_->Set("info", op_params_);

        if (paint) {
            this->addParam("paint", AsValue(*paint));
            filtered_paint_ = *paint;
        }

        if (canvas->flags_ & kOverdrawVisualization_Flag) {
            DCHECK(canvas->overdraw_xfermode_);

            filtered_paint_.setXfermode(canvas->overdraw_xfermode_);
            filtered_paint_.setAntiAlias(false);
        }

        start_ticks_ = base::TimeTicks::Now();
    }

    ~AutoOp()
    {
        base::TimeDelta ticks = base::TimeTicks::Now() - start_ticks_;
        op_record_->SetDouble("cmd_time", ticks.InMillisecondsF());

        canvas_->op_records_.Append(base::WrapUnique(op_record_));
    }

    void addParam(const char name[], std::unique_ptr<base::Value> value)
    {
        std::unique_ptr<base::DictionaryValue> param(new base::DictionaryValue());
        param->Set(name, std::move(value));

        op_params_->Append(std::move(param));
    }

    const SkPaint* paint() const { return &filtered_paint_; }

private:
    BenchmarkingCanvas* canvas_;
    base::DictionaryValue* op_record_;
    base::ListValue* op_params_;
    base::TimeTicks start_ticks_;

    SkPaint filtered_paint_;
};

BenchmarkingCanvas::BenchmarkingCanvas(SkCanvas* canvas, unsigned flags)
    : INHERITED(canvas->imageInfo().width(),
        canvas->imageInfo().height())
    , flags_(flags)
{
    addCanvas(canvas);

    if (flags & kOverdrawVisualization_Flag)
        overdraw_xfermode_ = sk_make_sp<OverdrawXfermode>();
}

BenchmarkingCanvas::~BenchmarkingCanvas()
{
}

size_t BenchmarkingCanvas::CommandCount() const
{
    return op_records_.GetSize();
}

const base::ListValue& BenchmarkingCanvas::Commands() const
{
    return op_records_;
}

double BenchmarkingCanvas::GetTime(size_t index)
{
    const base::DictionaryValue* op;
    if (!op_records_.GetDictionary(index, &op))
        return 0;

    double t;
    if (!op->GetDouble("cmd_time", &t))
        return 0;

    return t;
}

void BenchmarkingCanvas::willSave()
{
    AutoOp op(this, "Save");

    INHERITED::willSave();
}

SkCanvas::SaveLayerStrategy BenchmarkingCanvas::getSaveLayerStrategy(
    const SaveLayerRec& rec)
{
    AutoOp op(this, "SaveLayer", rec.fPaint);
    if (rec.fBounds)
        op.addParam("bounds", AsValue(*rec.fBounds));
    if (rec.fSaveLayerFlags)
        op.addParam("flags", SaveLayerFlagsAsValue(rec.fSaveLayerFlags));

    return INHERITED::getSaveLayerStrategy(rec);
}

void BenchmarkingCanvas::willRestore()
{
    AutoOp op(this, "Restore");

    INHERITED::willRestore();
}

void BenchmarkingCanvas::didConcat(const SkMatrix& m)
{
    AutoOp op(this, "Concat");
    op.addParam("matrix", AsValue(m));

    INHERITED::didConcat(m);
}

void BenchmarkingCanvas::didSetMatrix(const SkMatrix& m)
{
    AutoOp op(this, "SetMatrix");
    op.addParam("matrix", AsValue(m));

    INHERITED::didSetMatrix(m);
}

void BenchmarkingCanvas::onClipRect(const SkRect& rect,
    SkRegion::Op region_op,
    SkCanvas::ClipEdgeStyle style)
{
    AutoOp op(this, "ClipRect");
    op.addParam("rect", AsValue(rect));
    op.addParam("op", AsValue(region_op));
    op.addParam("anti-alias", AsValue(style == kSoft_ClipEdgeStyle));

    INHERITED::onClipRect(rect, region_op, style);
}

void BenchmarkingCanvas::onClipRRect(const SkRRect& rrect,
    SkRegion::Op region_op,
    SkCanvas::ClipEdgeStyle style)
{
    AutoOp op(this, "ClipRRect");
    op.addParam("rrect", AsValue(rrect));
    op.addParam("op", AsValue(region_op));
    op.addParam("anti-alias", AsValue(style == kSoft_ClipEdgeStyle));

    INHERITED::onClipRRect(rrect, region_op, style);
}

void BenchmarkingCanvas::onClipPath(const SkPath& path,
    SkRegion::Op region_op,
    SkCanvas::ClipEdgeStyle style)
{
    AutoOp op(this, "ClipPath");
    op.addParam("path", AsValue(path));
    op.addParam("op", AsValue(region_op));
    op.addParam("anti-alias", AsValue(style == kSoft_ClipEdgeStyle));

    INHERITED::onClipPath(path, region_op, style);
}

void BenchmarkingCanvas::onClipRegion(const SkRegion& region,
    SkRegion::Op region_op)
{
    AutoOp op(this, "ClipRegion");
    op.addParam("region", AsValue(region));
    op.addParam("op", AsValue(region_op));

    INHERITED::onClipRegion(region, region_op);
}

void BenchmarkingCanvas::onDrawPaint(const SkPaint& paint)
{
    AutoOp op(this, "DrawPaint", &paint);

    INHERITED::onDrawPaint(*op.paint());
}

void BenchmarkingCanvas::onDrawPoints(PointMode mode, size_t count,
    const SkPoint pts[], const SkPaint& paint)
{
    AutoOp op(this, "DrawPoints", &paint);
    op.addParam("mode", AsValue(mode));
    op.addParam("points", AsListValue(pts, count));

    INHERITED::onDrawPoints(mode, count, pts, *op.paint());
}

void BenchmarkingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint)
{
    AutoOp op(this, "DrawRect", &paint);
    op.addParam("rect", AsValue(rect));

    INHERITED::onDrawRect(rect, *op.paint());
}

void BenchmarkingCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint)
{
    AutoOp op(this, "DrawOval", &paint);
    op.addParam("rect", AsValue(rect));

    INHERITED::onDrawOval(rect, *op.paint());
}

void BenchmarkingCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint)
{
    AutoOp op(this, "DrawRRect", &paint);
    op.addParam("rrect", AsValue(rrect));

    INHERITED::onDrawRRect(rrect, *op.paint());
}

void BenchmarkingCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
    const SkPaint& paint)
{
    AutoOp op(this, "DrawDRRect", &paint);
    op.addParam("outer", AsValue(outer));
    op.addParam("inner", AsValue(inner));

    INHERITED::onDrawDRRect(outer, inner, *op.paint());
}

void BenchmarkingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint)
{
    AutoOp op(this, "DrawPath", &paint);
    op.addParam("path", AsValue(path));

    INHERITED::onDrawPath(path, *op.paint());
}

void BenchmarkingCanvas::onDrawPicture(const SkPicture* picture,
    const SkMatrix* matrix,
    const SkPaint* paint)
{
    DCHECK(picture);
    AutoOp op(this, "DrawPicture", paint);
    op.addParam("picture", AsValue(picture));
    if (matrix)
        op.addParam("matrix", AsValue(*matrix));

    INHERITED::onDrawPicture(picture, matrix, op.paint());
}

void BenchmarkingCanvas::onDrawBitmap(const SkBitmap& bitmap,
    SkScalar left,
    SkScalar top,
    const SkPaint* paint)
{
    AutoOp op(this, "DrawBitmap", paint);
    op.addParam("bitmap", AsValue(bitmap));
    op.addParam("left", AsValue(left));
    op.addParam("top", AsValue(top));

    INHERITED::onDrawBitmap(bitmap, left, top, op.paint());
}

void BenchmarkingCanvas::onDrawBitmapRect(const SkBitmap& bitmap,
    const SkRect* src,
    const SkRect& dst,
    const SkPaint* paint,
    SrcRectConstraint constraint)
{
    AutoOp op(this, "DrawBitmapRect", paint);
    op.addParam("bitmap", AsValue(bitmap));
    if (src)
        op.addParam("src", AsValue(*src));
    op.addParam("dst", AsValue(dst));

    INHERITED::onDrawBitmapRect(bitmap, src, dst, op.paint(), constraint);
}

void BenchmarkingCanvas::onDrawImage(const SkImage* image,
    SkScalar left,
    SkScalar top,
    const SkPaint* paint)
{
    DCHECK(image);
    AutoOp op(this, "DrawImage", paint);
    op.addParam("image", AsValue(*image));
    op.addParam("left", AsValue(left));
    op.addParam("top", AsValue(top));

    INHERITED::onDrawImage(image, left, top, op.paint());
}

void BenchmarkingCanvas::onDrawImageRect(const SkImage* image, const SkRect* src,
    const SkRect& dst, const SkPaint* paint,
    SrcRectConstraint constraint)
{
    DCHECK(image);
    AutoOp op(this, "DrawImageRect", paint);
    op.addParam("image", AsValue(*image));
    if (src)
        op.addParam("src", AsValue(*src));
    op.addParam("dst", AsValue(dst));

    INHERITED::onDrawImageRect(image, src, dst, op.paint(), constraint);
}

void BenchmarkingCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
    const SkIRect& center,
    const SkRect& dst,
    const SkPaint* paint)
{
    AutoOp op(this, "DrawBitmapNine", paint);
    op.addParam("bitmap", AsValue(bitmap));
    op.addParam("center", AsValue(SkRect::Make(center)));
    op.addParam("dst", AsValue(dst));

    INHERITED::onDrawBitmapNine(bitmap, center, dst, op.paint());
}

void BenchmarkingCanvas::onDrawText(const void* text, size_t byteLength,
    SkScalar x, SkScalar y,
    const SkPaint& paint)
{
    AutoOp op(this, "DrawText", &paint);
    op.addParam("count", AsValue(SkIntToScalar(paint.countText(text, byteLength))));
    op.addParam("x", AsValue(x));
    op.addParam("y", AsValue(y));

    INHERITED::onDrawText(text, byteLength, x, y, *op.paint());
}

void BenchmarkingCanvas::onDrawPosText(const void* text, size_t byteLength,
    const SkPoint pos[], const SkPaint& paint)
{
    AutoOp op(this, "DrawPosText", &paint);

    int count = paint.countText(text, byteLength);
    op.addParam("count", AsValue(SkIntToScalar(count)));
    op.addParam("pos", AsListValue(pos, count));

    INHERITED::onDrawPosText(text, byteLength, pos, *op.paint());
}

void BenchmarkingCanvas::onDrawPosTextH(const void* text, size_t byteLength,
    const SkScalar xpos[], SkScalar constY,
    const SkPaint& paint)
{
    AutoOp op(this, "DrawPosTextH", &paint);
    op.addParam("constY", AsValue(constY));

    int count = paint.countText(text, byteLength);
    op.addParam("count", AsValue(SkIntToScalar(count)));
    op.addParam("pos", AsListValue(xpos, count));

    INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, *op.paint());
}

void BenchmarkingCanvas::onDrawTextOnPath(const void* text, size_t byteLength,
    const SkPath& path, const SkMatrix* matrix,
    const SkPaint& paint)
{
    AutoOp op(this, "DrawTextOnPath", &paint);
    op.addParam("count", AsValue(SkIntToScalar(paint.countText(text, byteLength))));
    op.addParam("path", AsValue(path));
    if (matrix)
        op.addParam("matrix", AsValue(*matrix));

    INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, *op.paint());
}

void BenchmarkingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
    const SkPaint& paint)
{
    DCHECK(blob);
    AutoOp op(this, "DrawTextBlob", &paint);
    op.addParam("blob", AsValue(*blob));
    op.addParam("x", AsValue(x));
    op.addParam("y", AsValue(y));

    INHERITED::onDrawTextBlob(blob, x, y, *op.paint());
}

} // namespace skia
